package com.tsfyun.scm.declarechannel.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.tsfyun.scm.declarechannel.config.DeclareConfig;
import com.tsfyun.scm.declarechannel.config.DeclareNoticeSender;
import com.tsfyun.scm.declarechannel.config.NoticeProperties;
import com.tsfyun.scm.declarechannel.config.ProducerRabbitConfig;
import com.tsfyun.scm.declarechannel.dto.DeclareBizReceiveDTO;
import com.tsfyun.scm.declarechannel.dto.DeclareBizSendDTO;
import com.tsfyun.scm.declarechannel.entity.CusTenant;
import com.tsfyun.scm.declarechannel.service.ICusTenantService;
import com.tsfyun.scm.declarechannel.service.IDeclareService;
import com.tsfyun.scm.declarechannel.support.dingding.DingTalkNoticeUtil;
import com.tsfyun.scm.declarechannel.util.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class DeclareServiceImpl implements IDeclareService {

    @Autowired
    private ICusTenantService cusTenantService;
    @Autowired
    private DeclareNoticeSender declareNoticeSender;
    @Autowired
    private NoticeProperties noticeProperties;

    @Override
    public void receiver(DeclareBizSendDTO dto) {
        downFile(dto);
    }
    //下载报文
    public Boolean downFile(DeclareBizSendDTO dto){
        try{
            HttpRequest httpRequest = HttpRequest.get(dto.getDownUrl());
            httpRequest.setConnectionTimeout(10000).setReadTimeout(12000);
            HttpResponse response = httpRequest.execute();
            if(response.getStatus()==200){
                byte[] bytes =  response.bodyBytes();
                FileOutputStream fileOutputStream = new FileOutputStream(new File(("declare".equals(dto.getType())?DeclareConfig.DECCUS_PATH:DeclareConfig.RMFT_PATH)+"OutBox/"+dto.getFileName()));
                fileOutputStream.write(bytes);
                fileOutputStream.close();
                return Boolean.TRUE;
            }
            throw new RuntimeException("下载文件错误：状态"+response.getStatus());
        }catch (Exception e){
            log.error("下载文件失败：",e);
            //告知平台
            DingTalkNoticeUtil.send2DingDingOtherException(dto.getTenant(),"","报关渠道》报文下载失败",JSONObject.toJSONString(dto),e,noticeProperties.getDingdingUrl());
            return Boolean.FALSE;
        }
    }
    //扫描报关单回执数据
    public void scanDeclareRecFile(){
        String path = DeclareConfig.DECCUS_PATH+"InBox";
        try{
            List<DeclareBizReceiveDTO> receiveList = Lists.newArrayList();
            File dir = new File(path);
            File[] files = dir.listFiles();
            for(File file : files){
                SAXReader reader = new SAXReader();
                Document doc = reader.read(file);
                Element root = doc.getRootElement();
                List<Element> elements = root.elements();
                Map<String,String> map = xmlToMap(elements);
                log.info("扫描到报关单回执："+file.getName());
                if(file.getName().startsWith("Receipt_")){
                    //"CUS_CIQ_NO"://关检关联号 (数据中心统一编号)VARCHAR2(18)
                    //"ENTRY_ID"://报关单编号 (内网返回的报关单号)VARCHAR2(18)
                    //"NOTICE_DATE"://通知时间(yyyy-MM-ddTHH:mm:ss)VARCHAR2 (19)
                    //"CHANNEL"://处理结果VARCHAR2(1)
                    //"NOTE"://回执说明VARCHAR2(255)
                    //"CUSTOM_MASTER"://申报地海关VARCHAR2(4)
                    //"I_E_DATE"://进出口日期(yyyy-MM-dd)VARCHAR2(10)
                    //"D_DATE"://申报日期(yyyy-MM-ddTHH:mm:ss) VARCHAR2(19)

                    CusTenant cusTenant = cusTenantService.getId(map.get("CUS_CIQ_NO"));
                    if(Objects.nonNull(cusTenant)){
                        //发送消息
                        DeclareBizReceiveDTO dbr = new DeclareBizReceiveDTO();
                        dbr.setTenant(cusTenant.getTenant());
                        dbr.setBillNo(cusTenant.getDocNo());
                        dbr.setType("declare");
                        dbr.setChannel(map.get("CHANNEL"));
                        if(StringUtils.isEmpty(dbr.getChannel())){//转关
                            dbr.setChannel(map.get("PROC_RESULT"));
                        }
                        dbr.setStatus("0");
                        dbr.setNode(DeclareConfig.DECCUS_REC_STATUS_MAP.get(dbr.getChannel()));
                        if(StringUtils.isEmpty(dbr.getNode())){
                            dbr.setNode("-");
                        }
                        dbr.setMemo(map.get("NOTE"));
                        dbr.setCusCiqNo(cusTenant.getId());
                        dbr.setEntryId(map.get("ENTRY_ID"));
                        dbr.setNoticeDate(map.get("NOTICE_DATE"));
                        if(StringUtils.isEmpty(dbr.getNoticeDate())){
                            dbr.setNoticeDate(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
                        }
                        dbr.setIeDate(map.get("I_E_DATE"));
                        dbr.setDdate(map.get("D_DATE"));
                        dbr.setTransDclNo(map.get("TRANS_DCL_NO"));
                        dbr.setFileName(file.getName());

                        receiveList.add(dbr);
                    }else{
                        //转移文件
                        FileUtils.copyFileByNIO(file,new File( DeclareConfig.DECCUS_PATH+"unknown"+File.separator+file.getName()));
                        //删除文件
                        file.delete();
                    }
                }else if(file.getName().startsWith("Successed_")){
                    //"ResponseCode"://状态 0 成功
                    //"ErrorMessage"://消息
                    //"ClientSeqNo"://客户端编号，(租户_系统报关单号)
                    //"SeqNo"://关检关联号 成功才有 (数据中心统一编号)VARCHAR2(18)
                    String tenant = map.get("ClientSeqNo").split("_")[0];//租户
                    String billNo = map.get("ClientSeqNo").replace(tenant+"_","");//单据编号

                    //记录统一编号和租户
                    CusTenant ct = new CusTenant();
                    ct.setId(map.get("SeqNo"));
                    ct.setTenant(tenant);
                    ct.setDateCreated(LocalDateTime.now());
                    ct.setDocNo(billNo);
                    String cusId = file.getName().split("_")[1];
                    if(cusId.length()==32){
                        ct.setSource("tsfyun");
                        ct.setTenant(cusId);
                    }else{
                        ct.setSource("saas");
                    }
                    cusTenantService.saveExistenceUpdate(ct);

                    //发送消息
                    DeclareBizReceiveDTO dbr = new DeclareBizReceiveDTO();
                    dbr.setTenant(ct.getTenant());
                    dbr.setBillNo(billNo);
                    dbr.setType("declare");
                    dbr.setChannel("0");
                    dbr.setStatus("0");
                    dbr.setNode("暂存成功");
                    dbr.setMemo("单一窗口暂存成功");
                    dbr.setCusCiqNo(map.get("SeqNo"));
                    //通知服务器
                    declareNoticeSender.send(ProducerRabbitConfig.NOTICE_EXCHANGE_NAME,ProducerRabbitConfig.DECLARE_RECEIPT,dbr.getTenant(),JSONObject.toJSONString(dbr));
                    //转移文件
                    FileUtils.copyFileByNIO(file,new File( DeclareConfig.DECCUS_PATH+"processed/"+dbr.getTenant()+"/"+dbr.getBillNo()+"/"+file.getName()));
                    //删除文件
                    file.delete();
                }else if(file.getName().startsWith("Failed_")){
                    //"ResponseCode"://状态 0 成功
                    //"ErrorMessage"://消息
                    //"ClientSeqNo"://客户端编号，(租户_系统报关单号)
                    //"SeqNo"://关检关联号 成功才有 (数据中心统一编号)VARCHAR2(18)
                    String clientSeqNo = map.get("ClientSeqNo");
                    if(StringUtils.isEmpty(clientSeqNo)){
                        clientSeqNo = file.getName().split("_")[1].concat("_").concat(file.getName().split("_")[2]);
                    }
                    String tenant = clientSeqNo.split("_")[0];//租户
                    String billNo = clientSeqNo.replace(tenant+"_","");//单据编号
                    String cusId = file.getName().split("_")[1];
                    String source = "saas";
                    if(cusId.length()==32){
                        source = "tsfyun";
                        tenant = cusId;
                    }
                    //发送消息
                    DeclareBizReceiveDTO dbr = new DeclareBizReceiveDTO();
                    dbr.setTenant(tenant);
                    dbr.setBillNo(billNo);
                    dbr.setType("declare");
                    dbr.setChannel("0");
                    dbr.setStatus("1");
                    dbr.setNode("暂存失败");
                    if(!StringUtils.isEmpty(map.get("ResponseCode"))){
                        dbr.setMemo(map.get("ErrorMessage"));
                    }else{
                        //本地环境失败
                        //resultFlag
                        //failCode 错误码
                        //failInfo 原因
                        dbr.setMemo(map.get("failInfo"));
                        //转移文件
                        FileUtils.copyFileByNIO(file,new File( DeclareConfig.DECCUS_PATH+"unknown"+File.separator+file.getName()));
                        //钉钉通知告知平台
                        DingTalkNoticeUtil.send2DingDingOtherException(tenant,"","报关渠道》单据发送失败",dbr.getMemo(),null,noticeProperties.getDingdingUrl());
                    }
                    //通知服务器
                    declareNoticeSender.send(ProducerRabbitConfig.NOTICE_EXCHANGE_NAME,ProducerRabbitConfig.DECLARE_RECEIPT,dbr.getTenant(),JSONObject.toJSONString(dbr));
                    //转移文件
                    FileUtils.copyFileByNIO(file,new File( DeclareConfig.DECCUS_PATH+"processed/"+dbr.getTenant()+"/"+dbr.getBillNo()+"/"+file.getName()));
                    //删除文件
                    file.delete();
                }
            }
            if(CollUtil.isNotEmpty(receiveList)){
                receiveList = receiveList.stream().sorted(Comparator.comparing(DeclareBizReceiveDTO::getNoticeDate)).collect(Collectors.toList());

                receiveList.stream().forEach(dbr ->{
                    System.out.println(JSONObject.toJSONString(dbr));
                    String source = "saas";
                    if(dbr.getTenant().length()==32){
                        source = "tsfyun";
                    }
                    log.info(String.format("通知服务器【%s】:%s",source,JSONObject.toJSONString(dbr)));
                    //通知服务器
                    declareNoticeSender.send(ProducerRabbitConfig.NOTICE_EXCHANGE_NAME,ProducerRabbitConfig.DECLARE_RECEIPT,dbr.getTenant(),JSONObject.toJSONString(dbr));
                    File file = new File(path.concat("/").concat(dbr.getFileName()));
                    //转移文件
                    FileUtils.copyFileByNIO(file,new File( DeclareConfig.DECCUS_PATH+"processed/"+dbr.getTenant()+"/"+dbr.getBillNo()+"/"+file.getName()));
                    //删除文件
                    file.delete();
                });
            }
        }catch (Exception e){
            //钉钉通知告知平台
            DingTalkNoticeUtil.send2DingDingOtherException("","","报关渠道》报关单回执扫描失败","报关单回执扫描失败",e,noticeProperties.getDingdingUrl());
            log.error("扫描报关单回执数据失败：",e);
        }
    }
    //扫描舱单回执数据
    public void scanManifestRecFile(){
        String path = DeclareConfig.RMFT_PATH+"InBox";
        try{
            List<DeclareBizReceiveDTO> receiveList = Lists.newArrayList();
            File dir = new File(path);
            File[] files = dir.listFiles();
            for(File file : files){
                SAXReader reader = new SAXReader();
                Document doc = reader.read(file);
                Element root = doc.getRootElement();
                List<Element> elements = root.elements();
                Map<String,String> map = new LinkedHashMap<>();
                xmlAllNodeToMap(map,elements,null);
                log.info("扫描到舱单回执："+file.getName());
                String messageID = map.get("Head_MessageID");
                DeclareBizReceiveDTO dbr = new DeclareBizReceiveDTO();
                dbr.setTenant(messageID.split("_")[1]);
                dbr.setBillNo(messageID.split("_")[0]);
                dbr.setNoticeDate(map.get("Head_SendTime"));
                dbr.setFunctionCode(map.get("Head_FunctionCode"));
                if(file.getName().startsWith("Failed_")){
                    dbr.setStatus("1");//失败
                    dbr.setNode("申报失败");
                    dbr.setMemo(map.get("Response_AdditionalInformation_StatementDescription"));
                    dbr.setChannel(map.get("Response_AdditionalInformation_StatementCode"));
                    dbr.setFileName(file.getName());
                    receiveList.add(dbr);
                }else if(file.getName().startsWith("Successed_")){
                    dbr.setStatus("0");//成功
                    dbr.setNode("调用成功");
                    dbr.setMemo(map.get("Response_AdditionalInformation_StatementDescription"));
                    dbr.setChannel(map.get("Response_AdditionalInformation_StatementCode"));
                    dbr.setFileName(file.getName());
                    receiveList.add(dbr);
                }else if(file.getName().startsWith("Receipt_")){
                    dbr.setFileName(file.getName());
                    String channel = map.get("Response_ResponseType_Code");
                    if(StringUtils.isEmpty(channel)){
                        channel = map.get("Response_Consignment_AdditionalInformation_StatementCode");
                    }
                    if(StringUtils.isEmpty(channel)){
                        channel = map.get("Response_AdditionalInformation_StatementCode");
                    }
                    dbr.setChannel(channel);
                    if(StringUtils.isEmpty(channel)){
                        throw new Exception("未取到有效回执状态");
                    }
                    switch (channel){
                        case "E3":
                            dbr.setStatus("0");//成功
                            dbr.setNode("发往海关成功");
                            dbr.setMemo("发往海关成功");
                            break;
                        case "01":
                            dbr.setStatus("0");//成功
                            dbr.setNode("接受申报");
                            dbr.setMemo(map.get("Response_Consignment_AdditionalInformation_StatementDescription"));
                            break;
                        case "02":
                            dbr.setStatus("0");//成功
                            dbr.setNode("转人工审核");
                            dbr.setMemo(map.get("Response_Consignment_AdditionalInformation_StatementDescription"));
                            break;
                        case "03":
                            dbr.setStatus("1");//失败
                            dbr.setNode("报文填制不规范");
                            dbr.setMemo(map.get("Response_AdditionalInformation_StatementDescription"));
                            break;
                    }
                    receiveList.add(dbr);
                }
            }
            if(CollUtil.isNotEmpty(receiveList)) {

                receiveList = receiveList.stream().sorted(Comparator.comparing(DeclareBizReceiveDTO::getNoticeDate)).collect(Collectors.toList());

                receiveList.stream().forEach(dbr -> {
                    dbr.setType("mainfest");
                    dbr.setNoticeDate(DateUtil.format(DateUtil.parse(dbr.getNoticeDate(),"yyyyMMddHHmmssSSS"),"yyyy-MM-dd HH:mm:ss"));

                    String source = "saas";
                    if(dbr.getTenant().length()==32){
                        source = "tsfyun";
                    }
                    log.info(String.format("通知服务器【%s】:%s",source,JSONObject.toJSONString(dbr)));
                    //通知服务器
                    declareNoticeSender.send(ProducerRabbitConfig.NOTICE_EXCHANGE_NAME,ProducerRabbitConfig.DECLARE_RECEIPT,dbr.getTenant(),JSONObject.toJSONString(dbr));
                    File file = new File(path.concat("/").concat(dbr.getFileName()));
                    //转移文件
                    FileUtils.copyFileByNIO(file,new File( DeclareConfig.RMFT_PATH+"processed/"+dbr.getTenant()+"/"+dbr.getBillNo()+"/"+file.getName()));
                    //删除文件
                    file.delete();
                });
            }

        }catch (Exception e){
            //钉钉通知告知平台
            DingTalkNoticeUtil.send2DingDingOtherException("","","报关渠道》舱单回执扫描失败","舱单回执扫描失败",e,noticeProperties.getDingdingUrl());
            log.error("扫描舱单回执数据失败：",e);
        }
    }

    private Map<String,String> xmlToMap(List<Element> elements){
        Map<String,String> map = new LinkedHashMap<>();
        elements.stream().forEach(element -> {
            map.put(element.getName(),element.getText());
        });
        return map;
    }

    private void xmlAllNodeToMap(Map<String,String> map,List<Element> elements,String parentName){
        elements.stream().forEach(element -> {
            String key = StringUtils.isEmpty(parentName)?element.getName():String.format("%s_%s",parentName,element.getName());
            map.put(key,element.getText());
            List<Element> celementList = element.elements();
            if(CollUtil.isNotEmpty(celementList)){
                xmlAllNodeToMap(map,celementList,key);
            }
        });
    }

}
